#ifndef __SUBSYSTEM_BLOCK_ENTITIES_H__
#define __SUBSYSTEM_BLOCK_ENTITIES_H__

#include <vector>
#include <map>
#include <list>
#include <mutex>
#include <functional>

#include "Subsystem.hpp"
#include "entt/entt.hpp"
#include "nlohmann/json.hpp"
#include "blockingconcurrentqueue.h"


#include "Engine/Point3.hpp"
#include "Engine/GameGuid.h"

using namespace PocketSurvival::Engine;

namespace PocketSurvival
{
    struct ComponentBlockEntity;

    struct BlockEntityData
    {
        std::string entityName;
        Point3 point;
    };

    class SubsystemBlockEntities : public Subsystem
    {
    public:
        SubsystemBlockEntities();

        virtual void update(float dt) override;
        virtual void load(const nlohmann::json& json) override;
        virtual void save(nlohmann::json& json) override;

        virtual const std::string& getName() const override;
        virtual SubUpdateType getUpdateType() override;
        virtual SubLoadEchelon getLoadEchelon() override;

    public:
        void addBlockEntity(const std::string &entityName, const Point3 &point);
        void removeBlockEntity(ComponentBlockEntity* blockEntity);

        // 必须先对 blockEntitiesMutex 加锁后才能使用以下函数
        inline ComponentBlockEntity *findBlockEntity(const Point3 &point);
        inline ComponentBlockEntity *findBlockEntity(int32_t x, int32_t y, int32_t z);
        inline const std::map<Point3, ComponentBlockEntity*> &getBlockEntitiesMap();

    protected:
        void createBlockEntity(const nlohmann::json& jsonData);
        void internalSave();
        
    public:
        std::recursive_mutex blockEntitiesMutex;

    protected:
        std::map<Point3, ComponentBlockEntity*> m_blockEntitiesMap;

        moodycamel::BlockingConcurrentQueue<ComponentBlockEntity *> m_removeBlockEntityQueue;
        moodycamel::BlockingConcurrentQueue<BlockEntityData *> m_addBlockEntityQueue;
    };

    inline SubsystemBlockEntities::SubsystemBlockEntities() {}


    inline ComponentBlockEntity *SubsystemBlockEntities::findBlockEntity(const Point3 &point)
    {
        auto it = m_blockEntitiesMap.find(point);
        if (it != m_blockEntitiesMap.end())
        {
            return it->second;
        }
        return nullptr;
    }
    inline ComponentBlockEntity *SubsystemBlockEntities::findBlockEntity(int32_t x, int32_t y, int32_t z)
    {
        return findBlockEntity(Point3(x, y, z));
    }
    inline const std::map<Point3, ComponentBlockEntity*> &SubsystemBlockEntities::getBlockEntitiesMap()
    {
        return m_blockEntitiesMap;
    }


}

#endif